﻿using eLearning.WebServices;
using System;
using System.Collections.Generic;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Browser;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace AgLux
{
    /// <summary>
    /// Represents a control that displays a course navigator.
    /// </summary>

    public class Navigator : Control
    {
        public event EventHandler LeaveForIntroduction;
        public event EventHandler LeaveForSummary;

        private Canvas _mainArea, _previousArea, _header, _nextArea, _menuArea, _screenArea, _footer;

        private Canvas _previous, _next, _screen, _scroll, _scrollBar, _scrollRuler, _freeScroller, _upScroller, _downScroller, _showPrintable;
        private Shape _previousArrow, _nextArrow, _upArrow, _downArrow;
        private TextBlock _headline;

        private Structure _sectionTree;
        private bool _scrollIsActive, _nextIsPressed, _previousIsPressed;

        /// <summary>
        /// Initializes a new instance of the Navigator class.
        /// </summary>

        /// <param name="sectionTree">
        /// The tree structure of course sections.
        /// </param>

        public Navigator(Structure sectionTree)
            : base()
        {
            FrameworkElement resource = InitializeFromXaml(new StreamReader(GetType().Assembly.GetManifestResourceStream("eLearning.Navigator.xaml")).ReadToEnd());

            _mainArea = (Canvas)resource.FindName("mainArea");
            _previousArea = (Canvas)resource.FindName("previousArea");
            _header = (Canvas)resource.FindName("header");
            _nextArea = (Canvas)resource.FindName("nextArea");
            _menuArea = (Canvas)resource.FindName("menuArea");
            _screenArea = (Canvas)resource.FindName("screenArea");
            _footer = (Canvas)resource.FindName("footer");

            _previous = (Canvas)resource.FindName("previous");
            _next = (Canvas)resource.FindName("next");
            _screen = (Canvas)resource.FindName("screen");
            _scroll = (Canvas)resource.FindName("scroll");
            _scrollBar = (Canvas)resource.FindName("scrollBar");
            _scrollRuler = (Canvas)resource.FindName("scrollRuler");
            _freeScroller = (Canvas)resource.FindName("freeScroller");
            _upScroller = (Canvas)resource.FindName("upScroller");
            _downScroller = (Canvas)resource.FindName("downScroller");
            _showPrintable = (Canvas)resource.FindName("showPrintable");
            _previousArrow = (Shape)resource.FindName("previousArrow");
            _nextArrow = (Shape)resource.FindName("nextArrow");
            _upArrow = (Shape)resource.FindName("upArrow");
            _downArrow = (Shape)resource.FindName("downArrow");
            _headline = (TextBlock)resource.FindName("headline");

            _mainArea.Width = BrowserHost.ActualWidth;
            _mainArea.Height = BrowserHost.ActualHeight;
            _mainArea.MouseMove += new MouseEventHandler(CheckScroll);
            _mainArea.MouseLeftButtonUp += new MouseEventHandler(InactivateScroller);
            _mainArea.MouseLeave += new EventHandler(InactivateScroller);

            _previousArea.Width = _mainArea.Width * 1 / 10 - _mainArea.Width / 400;
            _previousArea.Height = _mainArea.Height * 1 / 8 - _mainArea.Width / 400;
            _previousArea.SetValue(Canvas.LeftProperty, 0);
            _previousArea.SetValue(Canvas.TopProperty, 0);
            _previousArea.MouseLeftButtonDown += new MouseEventHandler(PreviousPressed);
            _previousArea.MouseLeftButtonUp += new MouseEventHandler(PreviousReleased);
            _previousArea.MouseLeave += new EventHandler(PreviousLeft);

            _header.Width = _mainArea.Width * 8 / 10 - _mainArea.Width / 200;
            _header.Height = _mainArea.Height * 1 / 8 - _mainArea.Width / 400;
            _header.SetValue(Canvas.LeftProperty, _previousArea.Width + _mainArea.Width / 200);
            _header.SetValue(Canvas.TopProperty, 0);

            _nextArea.Width = _mainArea.Width * 1 / 10 - _mainArea.Width / 400;
            _nextArea.Height = _mainArea.Height * 1 / 8 - _mainArea.Width / 400;
            _nextArea.SetValue(Canvas.LeftProperty, _previousArea.Width + _header.Width + _mainArea.Width / 100);
            _nextArea.SetValue(Canvas.TopProperty, 0);
            _nextArea.MouseLeftButtonDown += new MouseEventHandler(NextPressed);
            _nextArea.MouseLeftButtonUp += new MouseEventHandler(NextReleased);
            _nextArea.MouseLeave += new EventHandler(NextLeft);

            _menuArea.Width = _mainArea.Width * 2 / 10 - _mainArea.Width / 400;
            _menuArea.Height = _mainArea.Height * 7 / 8 - _mainArea.Width / 400;
            _menuArea.SetValue(Canvas.LeftProperty, 0);
            _menuArea.SetValue(Canvas.TopProperty, _previousArea.Height + _mainArea.Width / 200);

            _screenArea.Width = _mainArea.Width * 8 / 10 - _mainArea.Width / 400;
            _screenArea.Height = _mainArea.Height * 6 / 8 - _mainArea.Width / 200;
            _screenArea.SetValue(Canvas.LeftProperty, _menuArea.Width + _mainArea.Width / 200);
            _screenArea.SetValue(Canvas.TopProperty, _previousArea.Height + _mainArea.Width / 200);

            _footer.Width = _mainArea.Width * 8 / 10 - _mainArea.Width / 400;
            _footer.Height = _mainArea.Height * 1 / 8 - _mainArea.Width / 400;
            _footer.SetValue(Canvas.LeftProperty, _menuArea.Width + _mainArea.Width / 200);
            _footer.SetValue(Canvas.TopProperty, _previousArea.Height + _screenArea.Height + _mainArea.Width / 100);

            _screen.Width = _screenArea.Width - _previousArea.Height * 2;
            _screen.Height = _screenArea.Height;
            _screen.SetValue(Canvas.LeftProperty, _menuArea.Width + _mainArea.Width / 200 + _previousArea.Height);
            _screen.SetValue(Canvas.TopProperty, _previousArea.Height + _mainArea.Width / 200);

            RectangleGeometry clip = new RectangleGeometry();
            clip.Rect = new Rect(0, 0, Convert.ToInt32(_screen.Width), Convert.ToInt32(_screen.Height));
            _screen.Clip = clip;

            _previous.SetValue(Canvas.LeftProperty, _previousArea.Width / 2 - _previous.Width / 2);
            _previous.SetValue(Canvas.TopProperty, _previousArea.Height / 2 - _previous.Height / 2);

            _next.SetValue(Canvas.LeftProperty, _nextArea.Width / 2 - _next.Width / 2);
            _next.SetValue(Canvas.TopProperty, _nextArea.Height / 2 - _next.Height / 2);

            _scroll.Height = _screenArea.Height;
            _scroll.SetValue(Canvas.LeftProperty, _screenArea.Width - _scroll.Width);

            _scrollBar.Height = _scroll.Height - _upScroller.Height - _downScroller.Height;
            _scrollBar.SetValue(Canvas.TopProperty, _upScroller.Height);
            _scrollBar.MouseLeftButtonDown += new MouseEventHandler(ActivateScroller);

            _scrollRuler.Height = _scrollBar.Height - _freeScroller.Height;

            _upScroller.MouseLeftButtonDown += new MouseEventHandler(ScrollUp);

            _downScroller.SetValue(Canvas.TopProperty, _scroll.Height - _downScroller.Height);
            _downScroller.MouseLeftButtonDown += new MouseEventHandler(ScrollDown);

            _showPrintable.SetValue(Canvas.LeftProperty, _footer.Width - _footer.Height / 2 - _showPrintable.Height / 2);
            _showPrintable.SetValue(Canvas.TopProperty, _footer.Height / 2 - _showPrintable.Height / 2);
            _showPrintable.MouseLeftButtonDown += new MouseEventHandler(ShowPrintablePressed);

            _sectionTree = sectionTree;

            double yCoordinate = 20;

            while (_sectionTree.NextSection() != null)
            {
                _sectionTree.CurrentSection.HeightChanged += new EventHandler(AdjustScroll);
                _sectionTree.CurrentSection.FullScreenLeft += new EventHandler(StandardSection);
                _sectionTree.CurrentSection.FullScreenEntered += new EventHandler(FullSection);

                TextBlock headline = new TextBlock();
                headline.Text = _sectionTree.CurrentHeadline;
                headline.TextWrapping = TextWrapping.Wrap;
                headline.Width = _menuArea.Width - 40;
                headline.Height = headline.ActualHeight;
                headline.SetValue(Canvas.LeftProperty, 20);
                headline.SetValue(Canvas.TopProperty, 0);
                headline.Foreground = new SolidColorBrush(Color.FromRgb(255, 255, 255));

                Canvas menuChoice = new Canvas();
                menuChoice.Width = _menuArea.Width;
                menuChoice.Height = headline.Height;
                menuChoice.SetValue(Canvas.LeftProperty, 0);
                menuChoice.SetValue(Canvas.TopProperty, yCoordinate);
                menuChoice.Background = new SolidColorBrush(Colors.Transparent);
                menuChoice.Children.Add(headline);
                menuChoice.MouseEnter += new MouseEventHandler(MenuEntered);
                menuChoice.MouseLeave += new EventHandler(MenuLeft);
                menuChoice.MouseLeftButtonDown += new MouseEventHandler(MenuPressed);

                _menuArea.Children.Add(menuChoice);

                yCoordinate += menuChoice.Height + 20;
            }

            _scrollIsActive = _nextIsPressed = _previousIsPressed = false;
        }

        /// <summary>
        /// Loads a course section.
        /// </summary>

        /// <param name="section">
        /// The section to be loaded.
        /// </param>

        public bool LoadSection(Section section)
        {
            bool loaded = false;

            if (section != null)
            {
                section.Prepare(_screen.Width);

                _screen.Children.Clear();
                _screen.Children.Add(section);

                _headline.Text = _sectionTree.CurrentHeadline;
                _headline.SetValue(Canvas.LeftProperty, _header.Width / 2 - _headline.ActualWidth / 2);
                _headline.SetValue(Canvas.TopProperty, _header.Height / 2 - _headline.ActualHeight / 2);

                loaded = true;
            }

            return loaded;
        }

        /// <summary>
        /// Occurs when the show printable button is pressed.
        /// </summary>

        /// <param name="sender">
        /// The object that raised the event.
        /// </param>

        /// <param name="args">
        /// The MouseEventArgs that contains the event data.
        /// </param>

        private void ShowPrintablePressed(object sender, MouseEventArgs args)
        {
            Service service = new Service();
            HtmlPage.Navigate(service.CreatePrintable("<font style=\"font-family:Arial; font-size:30px;\">" + _sectionTree.CurrentHeadline + "</font>" + _sectionTree.CurrentSection.Html), "");
            service.DeletePrintable();
        }

        /// <summary>
        /// Occurs when the height of a section is changed.
        /// </summary>

        /// <param name="sender">
        /// The object that raised the event.
        /// </param>

        /// <param name="args">
        /// The EventArgs that contains the event data.
        /// </param>

        private void AdjustScroll(object sender, EventArgs args)
        {
            if (_screen.Height < _sectionTree.CurrentSection.Height)
            {
                Scroll(0);
                _scroll.Visibility = Visibility.Visible;
            }

            else
            {
                _scroll.Visibility = Visibility.Collapsed;
                Scroll(0);
            }
        }

        /// <summary>
        /// Occurs when a section leaves full screen.
        /// </summary>

        /// <param name="sender">
        /// The object that raised the event.
        /// </param>

        /// <param name="args">
        /// The EventArgs that contains the event data.
        /// </param>

        private void StandardSection(object sender, EventArgs args)
        {
            foreach (UIElement child in _mainArea.Children)
            {
                child.Visibility = Visibility.Visible;
            }

            if (_screenArea.Height >= _sectionTree.CurrentSection.Height)
            {
                _scroll.Visibility = Visibility.Collapsed;
            }

            _screen.SetValue(Canvas.LeftProperty, _menuArea.Width + _mainArea.Width / 200 + _previousArea.Height);
            _screen.SetValue(Canvas.TopProperty, _previousArea.Height + _mainArea.Width / 200);

            RectangleGeometry clip = new RectangleGeometry();
            clip.Rect = new Rect(0, 0, Convert.ToInt32(_screen.Width), Convert.ToInt32(_screen.Height));
            _screen.Clip = clip;

            _sectionTree.CurrentSection.SetValue(Canvas.LeftProperty, 0);
            _sectionTree.CurrentSection.SetValue(Canvas.TopProperty, -(double)_freeScroller.GetValue(Canvas.TopProperty) * (_sectionTree.CurrentSection.Height - _screen.Height) / _scrollRuler.Height);
        }

        /// <summary>
        /// Occurs when a section enters full screen.
        /// </summary>

        /// <param name="sender">
        /// The object that raised the event.
        /// </param>

        /// <param name="args">
        /// The EventArgs that contains the event data.
        /// </param>

        private void FullSection(object sender, EventArgs args)
        {
            foreach (UIElement child in _mainArea.Children)
            {
                child.Visibility = Visibility.Collapsed;
            }

            _screen.SetValue(Canvas.LeftProperty, 0);
            _screen.SetValue(Canvas.TopProperty, 0);
            _screen.Visibility = Visibility.Visible;

            RectangleGeometry clip = new RectangleGeometry();
            clip.Rect = new Rect(0, 0, BrowserHost.ActualWidth, BrowserHost.ActualHeight);
            _screen.Clip = clip;
        }

        /// <summary>
        /// Occurs when the next button is pressed.
        /// </summary>

        /// <param name="sender">
        /// The object that raised the event.
        /// </param>

        /// <param name="args">
        /// The MouseEventArgs that contains the event data.
        /// </param>

        private void NextPressed(object sender, MouseEventArgs args)
        {
            _nextIsPressed = true;
        }

        /// <summary>
        /// Occurs when the next button is released.
        /// </summary>

        /// <param name="sender">
        /// The object that raised the event.
        /// </param>

        /// <param name="args">
        /// The MouseEventArgs that contains the event data.
        /// </param>

        private void NextReleased(object sender, MouseEventArgs args)
        {
            if (_nextIsPressed)
            {
                _nextIsPressed = false;
                _sectionTree.CurrentSection.End();

                if (!LoadSection(_sectionTree.NextSection()))
                {
                    OnLeaveForSummary(new EventArgs());
                }
            }
        }

        /// <summary>
        /// Occurs when the next button is left.
        /// </summary>

        /// <param name="sender">
        /// The object that raised the event.
        /// </param>

        /// <param name="args">
        /// The EventArgs that contains the event data.
        /// </param>

        private void NextLeft(object sender, EventArgs args)
        {
            _nextIsPressed = false;
        }

        /// <summary>
        /// Occurs when the previous button is pressed.
        /// </summary>

        /// <param name="sender">
        /// The object that raised the event.
        /// </param>

        /// <param name="args">
        /// The MouseEventArgs that contains the event data.
        /// </param>

        private void PreviousPressed(object sender, MouseEventArgs args)
        {
            _previousIsPressed = true;
        }

        /// <summary>
        /// Occurs when the previous button is released.
        /// </summary>

        /// <param name="sender">
        /// The object that raised the event.
        /// </param>

        /// <param name="args">
        /// The MouseEventArgs that contains the event data.
        /// </param>

        private void PreviousReleased(object sender, MouseEventArgs args)
        {
            if (_previousIsPressed)
            {
                _previousIsPressed = false;
                _sectionTree.CurrentSection.End();

                if (!LoadSection(_sectionTree.PreviousSection()))
                {
                    OnLeaveForIntroduction(new EventArgs());
                }
            }
        }

        /// <summary>
        /// Occurs when the previous button is left.
        /// </summary>

        /// <param name="sender">
        /// The object that raised the event.
        /// </param>

        /// <param name="args">
        /// The EventArgs that contains the event data.
        /// </param>

        private void PreviousLeft(object sender, EventArgs args)
        {
            _previousIsPressed = false;
        }

        /// <summary>
        /// Occurs when a menu choice is entered.
        /// </summary>

        /// <param name="sender">
        /// The object that raised the event.
        /// </param>

        /// <param name="args">
        /// The MouseEventArgs that contains the event data.
        /// </param>

        private void MenuEntered(object sender, MouseEventArgs args)
        {
            Canvas menuChoice = (Canvas)sender;
            TextBlock headline = (TextBlock)menuChoice.Children[0];

            menuChoice.Background = new SolidColorBrush(Colors.White);
            headline.Foreground = new SolidColorBrush(Colors.Black);
        }

        /// <summary>
        /// Occurs when a menu choice is left.
        /// </summary>

        /// <param name="sender">
        /// The object that raised the event.
        /// </param>

        /// <param name="args">
        /// The EventArgs that contains the event data.
        /// </param>

        private void MenuLeft(object sender, EventArgs args)
        {
            Canvas menuChoice = (Canvas)sender;
            TextBlock headline = (TextBlock)menuChoice.Children[0];

            menuChoice.Background = new SolidColorBrush(Colors.Transparent);
            headline.Foreground = new SolidColorBrush(Color.FromRgb(255, 255, 255));
        }

        /// <summary>
        /// Occurs when a menu choice is pressed.
        /// </summary>

        /// <param name="sender">
        /// The object that raised the event.
        /// </param>

        /// <param name="args">
        /// The MouseEventArgs that contains the event data.
        /// </param>

        private void MenuPressed(object sender, MouseEventArgs args)
        {
            _sectionTree.CurrentSection.End();

            Canvas menuChoice = (Canvas)sender;
            int index = _menuArea.Children.IndexOf(menuChoice);
            Section section = _sectionTree.FirstSection();

            for (int i = 0; i < index; i++)
            {
                section = _sectionTree.NextSection();
            }

            LoadSection(section);
        }

        /// <summary>
        /// Occurs when the mouse pointer is moving over the control.
        /// </summary>

        /// <param name="sender">
        /// The object that raised the event.
        /// </param>

        /// <param name="args">
        /// The MouseEventArgs that contains the event data.
        /// </param>

        private void CheckScroll(object sender, MouseEventArgs args)
        {
            if (_scrollIsActive)
            {
                Scroll(args.GetPosition(_scrollRuler).Y - _freeScroller.Height / 2);
            }
        }

        /// <summary>
        /// Occurs when is is time to activate scrolling.
        /// </summary>

        /// <param name="sender">
        /// The object that raised the event.
        /// </param>

        /// <param name="args">
        /// The MouseEventArgs that contains the event data.
        /// </param>

        private void ActivateScroller(object sender, MouseEventArgs args)
        {
            _scrollIsActive = true;
            CheckScroll(sender, args);
        }

        /// <summary>
        /// Occurs when is is time to inactivate scrolling.
        /// </summary>

        /// <param name="sender">
        /// The object that raised the event.
        /// </param>

        /// <param name="args">
        /// The EventArgs that contains the event data.
        /// </param>

        private void InactivateScroller(object sender, EventArgs args)
        {
            _scrollIsActive = false;
        }

        /// <summary>
        /// Scrolls the current section.
        /// </summary>

        /// <param name="yCoordinate">
        /// The Y coordinate which to scroll the section.
        /// </param>

        private void Scroll(double yCoordinate)
        {
            if (yCoordinate < 0)
            {
                yCoordinate = 0;
            }

            else if (yCoordinate > _scrollRuler.Height)
            {
                yCoordinate = _scrollRuler.Height;
            }

            _freeScroller.SetValue(Canvas.TopProperty, yCoordinate);
            _sectionTree.CurrentSection.SetValue(Canvas.TopProperty, -(double)_freeScroller.GetValue(Canvas.TopProperty) * (_sectionTree.CurrentSection.Height - _screen.Height) / _scrollRuler.Height);
        }

        /// <summary>
        /// Occurs when the scroll up arrow is pressed.
        /// </summary>

        /// <param name="sender">
        /// The object that raised the event.
        /// </param>

        /// <param name="args">
        /// The EventArgs that contains the event data.
        /// </param>

        private void ScrollUp(object sender, EventArgs args)
        {
            Scroll(-((double)_sectionTree.CurrentSection.GetValue(Canvas.TopProperty) + _screen.Height / 2) * _scrollRuler.Height / (_sectionTree.CurrentSection.Height - _screenArea.Height));
        }

        /// <summary>
        /// Occurs when the scroll down arrow is pressed.
        /// </summary>

        /// <param name="sender">
        /// The object that raised the event.
        /// </param>

        /// <param name="args">
        /// The EventArgs that contains the event data.
        /// </param>

        private void ScrollDown(object sender, EventArgs args)
        {
            Scroll(-((double)_sectionTree.CurrentSection.GetValue(Canvas.TopProperty) - _screen.Height / 2) * _scrollRuler.Height / (_sectionTree.CurrentSection.Height - _screenArea.Height));
        }

        /// <summary>
        /// Raises the LeaveForIntroduction event.
        /// </summary>

        /// <param name="args">
        /// The EventArgs that contains the event data.
        /// </param>

        private void OnLeaveForIntroduction(EventArgs args)
        {
            if (LeaveForIntroduction != null)
            {
                LeaveForIntroduction(this, args);
            }
        }

        /// <summary>
        /// Raises the LeaveForSummary event.
        /// </summary>

        /// <param name="args">
        /// The EventArgs that contains the event data.
        /// </param>

        private void OnLeaveForSummary(EventArgs args)
        {
            if (LeaveForSummary != null)
            {
                LeaveForSummary(this, args);
            }
        }
    }
}
